﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace daSyntaxAnalyser
{
    using daBase.Analyser;
    using daBase.Objects;
    using daBase.Objects.AnalysisErrors;
    using daDataLayer;
    using SyntaxTree;
    using Analysers;
    using Analysers.DfmAnalyser;

    /// <summary>
    /// Контроллер синтаксического анализа
    /// </summary>
    public class SyntaxAnalyserController : BaseAnalysisController
    {
        // корни деревьев, ссылки на модули и имена файлов (в нижнем регистре)
        private List<FileElement> roots;
        private Dictionary<string, SyntaxTreeLink> links;
        private List<string> lowfilenames;

        // шаблон "одиночка"
        private static SyntaxAnalyserController instance;

        /// <summary>
        /// Единственный экземпляр класса контроллера
        /// </summary>
        public static SyntaxAnalyserController Instance
        {
            get
            {
                if (instance == null)
                    instance = new SyntaxAnalyserController();
                return instance;
            }
        }

        /// <summary>
        /// Сообщение "Файл проанализирован"
        /// </summary>
        public event EventHandler<SourceArgs> FileAnalysedEvent;

        /// <summary>
        /// Обработчик события "файл проанализирован"
        /// </summary>
        /// <param name="sender">инициатор события</param>
        /// <param name="e">информация о файле</param>
        public void FileAnalysed(object sender, SourceArgs e)
        {
            if (FileAnalysedEvent != null)
                FileAnalysedEvent(sender, e);
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        public SyntaxAnalyserController()
        {
            links = new Dictionary<string, SyntaxTreeLink>();
            DfmLexicalAnalyser.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            DfmSyntaxAnalyser.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            LexicalAnalyser.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            SyntaxAnalysers.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            LexicalAnalyser.Instance.FileAnalysedEvent += new EventHandler<SourceArgs>(FileAnalysed);
        }

        /// <summary>
        /// Трансляция файла
        /// </summary>
        /// <param name="filename">имя файла</param>
        /// <param name="loadIfNeed">необходимо ли подгружать объявленные модули, если их нет в проекте</param>
        /// <returns>ссылка на корень дерева файла</returns>
        public SyntaxTreeLink Translate(string filename, bool loadIfNeed)
        {
            string lowfilename = filename.ToLower();
            if (links.ContainsKey(lowfilename))
            {
                if (roots.Contains(links[lowfilename].Root)) roots.Remove(links[lowfilename].Root);
                return links[lowfilename];
            }
            else if (File.Exists(filename) && (lowfilenames.Contains(lowfilename) || loadIfNeed))
            {
                links.Add(lowfilename, new SyntaxTreeLink());
                SyntaxAnalyser analyser = SyntaxAnalysers.Instance.Pop();
                links[lowfilename].Root = analyser.Translate(filename, loadIfNeed);
                SyntaxAnalysers.Instance.Push(analyser);
                return links[lowfilename];
            }
            else return null;
        }

        /// <summary>
        /// Трансляция проекта (набора файлов)
        /// </summary>
        /// <param name="filenames">список файлов в проекте анализа</param>
        /// <param name="loadIfNeed">необходимо ли подгружать объявленные модули, если их нет в проекте</param>
        /// <returns>список корней синтаксических деревьев</returns>
        public List<FileElement> Translate(List<string> filenames, bool loadIfNeed)
        {
            FileElement root;
            links.Clear();
            lowfilenames = filenames.ConvertAll<string>(filename => filename.ToLower());
            roots = new List<FileElement>();
            LexicalAnalyser.FileSLOC.Clear();
            LexicalAnalyser.FileEmptyLiles.Clear();
            foreach (string filename in filenames)
            {
                if (File.Exists(filename) && !links.ContainsKey(filename.ToLower()))
                {
                    root = Translate(filename, loadIfNeed).Root;
                    if (root != null)
                        roots.Add(root);
                }
            }
            return roots;
        }
    }
}
